//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <util.h>
#include <udelay.h>
#include <gpio.h>
#include <bulverde.h>

// I2C control registers
#define BV_IBMR        __REG(0x40301680)          //    Bus Monitor register
#define BV_IDBR         __REG(0x40301688)          //    Data Buffer register
#define BV_ICR         __REG(0x40301690)         //    Control register
#define BV_ISR          __REG(0x40301698 )          //    Status register
#define BV_ISAR         __REG(0x403016A0 )         //    Slave Address register

#define I2C_ICR_UR      (1<<14)  // Unit Reset
#define I2C_ICR_ALDIE   (1<<12)  // Arbitration-Loss-Detected interrupt enable
#define I2C_ICR_GCD     (1<<7)   // General Call Disable
#define I2C_ICR_IUE     (1<<6)   // I2C Unit Enable
#define I2C_ICR_SCLEA   (1<<5)   // SCL Enable for master-mode operation
#define I2C_ICR_MA      (1<<4)   // Master Abort without transmitting another data byte
#define I2C_ICR_TB      (1<<3)   // Transfer Byte
#define I2C_ICR_ACKNAK  (1<<2)   // '0' for ACK & '1' for NAK
#define I2C_ICR_STOP    (1<<1)   // A STOP condition after next data byte
#define I2C_ICR_START   (1<<0)   // Initiate a START condition

#define I2C_ISR_BED     (1<<10)  // Bus Error Detected
#define I2C_ISR_IRF     (1<<7)   // IDBR Receive Full
#define I2C_ISR_ITE     (1<<6)   // IDBR Transmit Empty
#define I2C_ISR_ALD     (1<<5)   // Arbitration Loss Detected
#define I2C_ISR_IBB     (1<<3)   // I2C Bus Busy
#define I2C_ISR_UB      (1<<2)   // Unit Busy

#define CLK_I2C_ENABLE (CKEN |= CKEN14_I2C)
#define CLK_I2C_DISABLE (CKEN &= ~CKEN14_I2C)

#define I2C_SCL_GPIO117	(1<<21)
#define I2C_SDA_GPIO118 (1<<22)
#define I2CPERIOD   4
#define CAMERA_I2CWRITE_ADDR   0x60
#define CAMERA_I2CREAD_ADDR    0x61
#define I2C_MAX_XFER_WAIT      0x1000
#define I2C_MAX_BUS_WAIT       0x1000

void I2C_GPIO(void)
{
//    GAFR3_U &= ~((3<<10)|(3<<12));
    GAFR3_U |= (1<<10)|(1<<12);
    //GPDR3 |= I2C_SCL_GPIO117|I2C_SDA_GPIO118;
	//GPSR3 |= I2C_SCL_GPIO117|I2C_SDA_GPIO118;
}

void I2C_enable(void)
{
	BV_ISAR = 0;      // Slave Address
	BV_ICR = I2C_ICR_GCD | I2C_ICR_IUE | I2C_ICR_SCLEA; /*100 kbps*/
  	//BV_ICR = I2C_ICR_GCD | I2C_ICR_IUE | I2C_ICR_SCLEA | (1<<15)/*400 kbps*/;
}

void I2C_Initial(void)
{
    CLK_I2C_ENABLE;      //enable i2c clock
	I2C_GPIO();
    I2C_enable();
}

void I2C_Disable(void)
{
  	BV_ICR &= ~I2C_ICR_IUE; // Disable I2C Unit

    CLK_I2C_DISABLE;
}

static int I2C_wait_for_tx(void)
{
    int retry = I2C_MAX_XFER_WAIT;

    while(((BV_ISR & I2C_ISR_ITE)==0)&&(retry--))
    {
	    udelay(100);       // sleep 100 milisecond
	    //printk("I2C ISR :%x\r\n",ISR);
    }

    if (retry < 0)
    {
	    printk("Timeout waiting for tx \r\n");
    }
	else{
		BV_ISR = I2C_ISR_ITE;
		(void)BV_ISR;
	}
    return ((retry<0) ? -1 : 0);
}

static int I2C_wait_for_rx(void)
{
    int retry = I2C_MAX_XFER_WAIT;

    while((!(BV_ISR & I2C_ISR_IRF))&& (retry--))
    {
	    udelay(100);
    }

    if (retry < 0)
    {
	    printk("Timeout waiting for rx \n");
    }
    else{
	    BV_ISR = I2C_ISR_IRF;
	    (void) BV_ISR;
    }
    return ((retry<0) ? -1 : 0);
}

static int I2C_wait_for_bus(void)
{
    int retry = I2C_MAX_BUS_WAIT;
    unsigned long isr = BV_ISR;
    while ( (isr & I2C_ISR_IBB) && (isr & I2C_ISR_UB) && retry--)
    {
	    udelay(100);
	    isr = BV_ISR;
    }

    if (retry <0)
    {
	    printk("Timeout waiting for bus busy \n");
    }
    return ((retry<0) ? -1 : 0);
}

struct i2c_msg {
    char addr;
    char flags;
    char *buf;
    int len;
};

#define I2C_M_RD 1
int I2C_transfer(struct i2c_msg msg[], int num)
{
    int ret=0;
    int addr;
    struct i2c_msg *pmsg = msg;

    ret  = I2C_wait_for_bus();

    if(ret)
    {
	    printk("bus busy\r\n");
	    goto out;
    }

    while(num--)
    {
	addr = pmsg->addr ;
	if (pmsg->flags & I2C_M_RD)
	{
	    addr |=1;
	}
	else
	{
	    addr &= ~1;
	}

//address of slave
	BV_IDBR = addr;
//initiate write
	BV_ICR &= ~(I2C_ICR_STOP);
	BV_ICR |= (I2C_ICR_START | I2C_ICR_TB);

	ret = I2C_wait_for_tx();
	if(ret < 0)
	{
	    printk ("init tx time out \r\n");
	    goto out;
	}
//start to transfer
	if (pmsg->flags & I2C_M_RD) //read bytes
	{
	    int count;

		BV_ICR &= (~(I2C_ICR_START | I2C_ICR_STOP));

	    for(count=0 ; count<pmsg->len; count++)
	    {
		if (count+1 == pmsg->len)
		{
			//last byte to read
		    BV_ICR |= ( I2C_ICR_STOP | I2C_ICR_TB| I2C_ICR_ACKNAK);
		}
		else
		{
		    BV_ICR |= (I2C_ICR_TB);/* Read */
		}

		/*
		while((ISR&ISR_IRF) == 0){}

		ISR = ISR_IRF;
		(void) ISR;

	*/
		ret = I2C_wait_for_rx();

		if(ret)
		{
		    printk("i2c_read: timeout\r\n");
		    goto out;
		}
		pmsg->buf[count]=(char)BV_IDBR;

		(BV_ICR) &=~ ( I2C_ICR_ACKNAK );

	    }


	}
	else //write bytes
	{
	    int count;

	    for(count=0;count<pmsg->len;count++)
	    {

		BV_IDBR=pmsg->buf[count];

		BV_ICR &= (~(I2C_ICR_START|I2C_ICR_STOP));

		if (count+1 == pmsg->len)
		{
		    BV_ICR |= (I2C_ICR_TB|I2C_ICR_STOP);
		}
		else{
		    BV_ICR |= (I2C_ICR_TB);
		}
	/*
		while((ISR&ISR_ITE) == 0){}

		ISR = ISR_ITE;
		(void) ISR;
	*/

		ret = I2C_wait_for_tx();
		if(ret<0)
		{
		    printk("i2c_write: timeout\r\n");
		    goto out;
		}

	    }

	}
	pmsg += 1;
    }

	 // while( (ISR & ISR_IBB) || (ISR & ISR_UB) ) { ; }
 out:
    return ret;
}


int I2C_master_send(const char * pbuf, int count)
{
  int ret;
  struct i2c_msg msg;

  msg.addr = (char)CAMERA_I2CWRITE_ADDR;
  msg.flags = 0;
  msg.buf = (char*)pbuf;
  msg.len = count;

  ret = I2C_transfer(&msg, 1);
  return ret;
}

int I2C_master_recv(char * pbuf, int count)
{
  int ret;
  struct i2c_msg msg;

  msg.addr = (char)CAMERA_I2CREAD_ADDR;
  msg.flags = I2C_M_RD;
  msg.buf = pbuf;
  msg.len = count;

  ret = I2C_transfer(&msg, 1);
  return ret;
}


int  I2C_write_byte(char regaddr,char val)
{
    char buf[8];
    buf[0] = regaddr;
    buf[1] = val;
    return I2C_master_send(buf,2);
}

int  I2C_read_byte(char regaddr,char *buf)
{
    char addbuf[2];

    addbuf	[0] = regaddr;
    if( I2C_master_send(addbuf,1) )
    {
            printk("i2c send address fail\r\n");
	     return -1;
    }
    if( I2C_master_recv(buf,1) )
    {
            printk("i2c receive  address fail\r\n");
	     return -1;
    }
    return 0;
}
